using Engine.AddinManager;
using Engine.Graphics;
using Engine.Graphics.VertexStructures;
using SharpDX;
using System;
using System.Collections.Generic;
using System.IO;

namespace Engine.FileFormats3D
{
    public struct Mesh3dData
    {
        #region Public Fields

        public readonly object thisLock;

        #endregion Public Fields

        #region Private Fields

        private AddinManager.AddinManager addinManager;
        private List<ushort> indices;
        private MaterialStorage materialStorage;
        private List<int> polyTextures;
        private List<MyOwnVertex.PositionNormalTextured> vertices;

        /// <summary>
        /// stored the advanced vertices information for different addins.
        /// </summary>
        private Dictionary<string, List<IGameVertex>> verticesWr2Data;

        #endregion Private Fields

        #region Public Constructors

        public Mesh3dData(AddinManager.AddinManager addinManager, AddinManager.IAddin addin, List<Material> materials)
        {
            this.thisLock = new object();
            this.indices = new List<ushort>();
            this.vertices = new List<MyOwnVertex.PositionNormalTextured>();
            this.verticesWr2Data = new Dictionary<string, List<IGameVertex>>();
            this.polyTextures = new List<int>();

            for (int i = 0; i < materials.Count; i++)
            {
                this.polyTextures.Add(0);
            }
            this.addinManager = addinManager;

            // Add the materials to the materialstorage.
            this.materialStorage = new MaterialStorage();
            this.materialStorage.AddAddinName(addin.GetGameName());
            foreach (var material in materials)
            {
                string materialName = material.MaterialName;
                if (materialName == string.Empty)
                {
                    materialName = "Material " + this.materialStorage._materialsAddin.Count.ToString();
                    Console.WriteLine("No Materialname given for mesh3ddata creation");
                }

                this.materialStorage.AddMaterial(addin.GetGameName(), materialName, material.GameMaterialProperties);
            }
        }

        public Mesh3dData(MaterialManager materialManager, Meshobject mesh, AddinManager.AddinManager addinManager, AddinManager.IAddin addin)
        {
            this.thisLock = new object();
            this.indices = new List<ushort>(mesh.Indices);
            this.vertices = new List<MyOwnVertex.PositionNormalTextured>(mesh.Vertices);
            this.verticesWr2Data = new Dictionary<string, List<IGameVertex>>();
            this.verticesWr2Data.Add(addin.GetGameName(), new List<IGameVertex>(mesh.VerticesWr2Data));

            this.polyTextures = new List<int>(mesh.UsedPolysForMaterial);

            this.addinManager = addinManager;

            // Add the materials to the materialstorage.
            this.materialStorage = new MaterialStorage();

            for (int i = 0; i < mesh.materialsIDs.Length; i++)
            {
                var material = materialManager.GetMaterial(mesh.materialsIDs[i]);
                this.materialStorage.AddMaterial(addin.GetGameName(), material.MaterialName, material.GameMaterialProperties);
            }

            foreach (var materialID in mesh.materialsIDs)
            {
                // Get material by materialID.
                var material = materialManager.GetMaterial(materialID);

                string materialName = material.MaterialName;
                if (materialName == string.Empty)
                {
                    materialName = "Material " + this.materialStorage._materialsAddin.Count.ToString();
                    Console.WriteLine("No Materialname given for mesh3ddata creation");
                }

                this.materialStorage.AddMaterial(addin.GetGameName(), materialName, material.GameMaterialProperties);
            }
        }

        public Mesh3dData(List<ushort> indices, List<MyOwnVertex.PositionNormalTextured> vertices, List<IGameVertex> verticesWr2Data,
            List<int> numberOfPolysForTexture, List<Material> materials, AddinManager.AddinManager addinManager, AddinManager.IAddin addin)
        {
            this.thisLock = new object();
            this.indices = new List<ushort>(indices);
            this.vertices = new List<MyOwnVertex.PositionNormalTextured>(vertices);
            this.verticesWr2Data = new Dictionary<string, List<IGameVertex>>();
            this.verticesWr2Data.Add(addin.GetGameName(), new List<IGameVertex>(verticesWr2Data));

            this.polyTextures = new List<int>(numberOfPolysForTexture);
            this.addinManager = addinManager;

            // Add the materials to the materialstorage.
            this.materialStorage = new MaterialStorage();
            foreach (var material in materials)
            {
                string materialName = material.MaterialName;
                if (materialName == string.Empty)
                {
                    materialName = "Material " + this.materialStorage._materialsAddin.Count.ToString();
                    Console.WriteLine("No Materialname given for mesh3ddata creation");
                }

                this.materialStorage.AddMaterial(addin.GetGameName(), materialName, material.GameMaterialProperties);
            }
        }

        public List<MaterialItem> GetMaterials(string addinName)
        {
            foreach (var item in this.materialStorage._materialsAddin)
            {
                if (item.Addinname == addinName)
                    return item.Materials;
            }
            throw new Exception("Materials for this game is not found");
        }

        #endregion Public Constructors

        #region Public Properties

        public List<ushort> Indices
        {
            get { return this.indices; }
            set { this.indices = value; }
        }

        public List<int> PolyTextures
        {
            get { return this.polyTextures; }
            set { this.polyTextures = value; }
        }

        public List<MyOwnVertex.PositionNormalTextured> Vertices
        {
            get { return this.vertices; }
            set { this.vertices = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public static Mesh3dData CreateForestBorder(Vector3[] middle, decimal anglePointOne, decimal anglePointTwo,
                    float widthPointOne, float widthPointTwo, float inclinationPointOne, float inclinationPointTwo,
                    ref float textureUcoordinates, float textureScale, Material materialID_For_Trees, AddinManager.AddinManager addinManager, AddinManager.IAddin addin, out float[] middleLineDistances,
                    out float lengthOfStreet)
        {
            float textureUcoordinatesData = textureUcoordinates;
            Array.Reverse(middle);
            List<int> polyTextures = new List<int>();
            List<ushort> indices = new List<ushort>();
            List<MyOwnVertex.PositionNormalTextured> vertices = new List<MyOwnVertex.PositionNormalTextured>();
            short OffsetIndices = (short)(vertices.Count);

            polyTextures.Add((middle.Length - 1) * 4);

            // This calculates the distances between each Vectors from middleline
            middleLineDistances = new float[middle.Length];
            lengthOfStreet = 0;
            middleLineDistances[0] = GeoMath.DistanceBtw2Points(middle[0], middle[1]);
            lengthOfStreet += middleLineDistances[0];
            for (int i2 = 1; i2 < middle.Length - 1; i2++)
            {
                middleLineDistances[i2] = GeoMath.DistanceBtw2Points(middle[i2], middle[i2 + 1]);
                lengthOfStreet += middleLineDistances[i2];
            }

            // here will the indicies calculate
            short point = 0;
            for (short i2 = 0; i2 < (middle.Length - 1) * 6; i2 += 6)
            {
                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));

                point += 2;
            }

            int i = 0;

            Vector3 rightPoint; //Is a tempoary Point
            float percentofStreet = 0;
            float streetwidth;
            float inclination = 0;

            //start
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointOne, widthPointOne, inclinationPointOne);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0],
                Normal = Vector3.UnitY,
                UV = new Vector2(textureUcoordinatesData, 0f)
            });
            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] + rightPoint,
                Normal = Vector3.UnitY,
                UV = new Vector2(textureUcoordinatesData, -1f)
            });

            textureUcoordinatesData -= (middleLineDistances[0] / widthPointOne) * textureScale;

            for (i = 1; i < (middle.Length - 1); i++)
            {
                percentofStreet += middleLineDistances[i - 1];
                streetwidth = widthPointOne - ((widthPointOne - widthPointTwo) * (percentofStreet / lengthOfStreet));
                inclination = inclinationPointOne + ((inclinationPointTwo - inclinationPointOne) * (percentofStreet / lengthOfStreet));

                rightPoint = GeoMath.makepoint(middle[i + 1], middle[i - 1], streetwidth, inclination);

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i],
                    Normal = Vector3.UnitY,
                    UV = new Vector2(textureUcoordinatesData, 0f)
                });
                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] + rightPoint,
                    Normal = Vector3.UnitY,
                    UV = new Vector2(textureUcoordinatesData, -1f)
                });

                textureUcoordinatesData -= (middleLineDistances[i] / streetwidth) * textureScale;
            }
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointTwo - 180, widthPointTwo, inclinationPointTwo);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1],
                Normal = Vector3.UnitY,
                UV = new Vector2(textureUcoordinatesData, 0f)
            });
            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] + rightPoint,
                Normal = Vector3.UnitY,
                UV = new Vector2(textureUcoordinatesData, -1f)
            });

            //List<IGameVertex> verticesWr2Data = CreateGameVerticesForForest(middle, widthPointOne, widthPointTwo, textureScale, textureUcoordinatesData, middleLineDistances, lengthOfStreet);

            //throw new NotImplementedException();
            //addin.CreateVerticesForestBorder();

            return new Mesh3dData(indices, vertices, new List<IGameVertex>(vertices.Count), polyTextures, new List<Material>() { materialID_For_Trees }, addinManager, addin);
        }

        public static Mesh3dData FromStream(BinaryReader br, AddinManager.AddinManager addinManager, MaterialManager materialManager, string filenameModel, MaterialStorage materialStorage)
        {
            // Read the Header (26 Bytes)
            string ident = new string(br.ReadChars(14));

            switch (ident)
            {
                case "MeshForAddin 1":
                    var addin = addinManager.GetAddin("World Racing 2");
                    return LoadV1(br, addin, addinManager, materialManager);

                case "MeshForAddin 2":
                    return LoadV2(br, addinManager, materialManager, filenameModel);

                case "MeshForAddin 3":
                    return LoadV3(br, addinManager, materialManager, filenameModel);

                case "MeshForAddin 4":
                    return LoadV4(br, addinManager, materialManager, materialStorage);

                default:
                    throw new Exception("Unknown file version");
            }
        }

        public static Mesh3dData Transform(ref Mesh3dData mesh, decimal angle, Vector3 centerOfCross)
        {
            Mesh3dData transformedMesh = new Mesh3dData();

            lock (mesh.thisLock)
            {
                transformedMesh.vertices = new List<MyOwnVertex.PositionNormalTextured>(mesh.Vertices);
                transformedMesh.verticesWr2Data = new Dictionary<string, List<IGameVertex>>();
                // Copy old gamevertices to new dictionary.
                foreach (var item in mesh.verticesWr2Data)
                {
                    transformedMesh.verticesWr2Data.Add(item.Key, new List<IGameVertex>(item.Value));
                }

                transformedMesh.Indices = new List<ushort>(mesh.Indices);
                MaterialStorage materialStorage = new MaterialStorage();
                mesh.materialStorage.CopyTo(materialStorage);
                transformedMesh.materialStorage = materialStorage;

                transformedMesh.PolyTextures = new List<int>(mesh.PolyTextures);
                MyOwnVertex.PositionNormalTextured vertex;

                Matrix matrix = Matrix.Identity;
                matrix = Matrix.RotationY((float)angle * (float)(Math.PI / 180));

                for (int v = 0; v < transformedMesh.Vertices.Count; v++)
                {
                    vertex = transformedMesh.Vertices[v];
                    vertex.Position = Vector3.TransformCoordinate(vertex.Position, matrix);
                    vertex.Position = Vector3.Multiply(vertex.Position, 1.0f) + centerOfCross;
                    transformedMesh.Vertices[v] = vertex;
                }
            }
            return transformedMesh;
        }

        public static Mesh3dData Transform(ref Mesh3dData mesh, ref List<Vector3> middleline, ref List<Vector2> inclinationVectors, bool inverseInclination,
            ref Vector3 directionStart, ref Vector3 directionEnd, bool ScaleToSegmentLength, float minX, float maxX)
        {
            float xOffset = 0;
            Mesh3dData transformedMesh = new Mesh3dData();

            lock (mesh.thisLock)
            {
                transformedMesh.vertices = new List<MyOwnVertex.PositionNormalTextured>(mesh.Vertices);
                transformedMesh.verticesWr2Data = new Dictionary<string, List<IGameVertex>>();
                // Copy old gamevertices to new dictionary.
                foreach (var item in mesh.verticesWr2Data)
                {
                    transformedMesh.verticesWr2Data.Add(item.Key, new List<IGameVertex>(item.Value));
                }

                transformedMesh.Indices = new List<ushort>(mesh.Indices);
                MaterialStorage materialStorage = new MaterialStorage();
                mesh.materialStorage.CopyTo(materialStorage);
                transformedMesh.materialStorage = materialStorage;
                transformedMesh.PolyTextures = new List<int>(mesh.PolyTextures);

                // Calculate distances between
                List<float> distancesMiddleLine = new List<float>();
                List<Vector3> directionToNextPoint = new List<Vector3>();
                List<Vector3> middleLinePositionForEachVertex = new List<Vector3>();
                float distance = 0;

                for (int i = 0; i < transformedMesh.vertices.Count; i++)
                {
                    xOffset = Math.Min(xOffset, transformedMesh.Vertices[i].X);
                }

                /* Use the smallest X value. */
                xOffset = Math.Min(minX, xOffset);

                //transformedMesh.ScaleX(2.0f);
                float meshLengthX = 0;
                for (int i = 0; i < transformedMesh.vertices.Count; i++)
                {
                    MyOwnVertex.PositionNormalTextured vertex = transformedMesh.Vertices[i];
                    vertex.X = Math.Max(0, vertex.X - xOffset);
                    transformedMesh.Vertices[i] = vertex;
                    meshLengthX = Math.Max(meshLengthX, vertex.X);
                }

                /* Get the complete Segment Length. */
                meshLengthX = Math.Max(maxX - minX, meshLengthX);

                distancesMiddleLine.Add(0); // first Point starts at zero.
                for (int i = 0; i < middleline.Count - 1; i++)
                {
                    distance += GeoMath.DistanceBtw2Points(middleline[i], middleline[i + 1]);
                    distancesMiddleLine.Add(distance);
                }

                if (ScaleToSegmentLength && distancesMiddleLine[1] > 0.0f)
                {
                    transformedMesh.ScaleX(distancesMiddleLine[1] / meshLengthX);
                    for (int i = 0; i < transformedMesh.Vertices.Count; i++)
                    {
                        MyOwnVertex.PositionNormalTextured temp = transformedMesh.Vertices[i];
                        temp.Position.X = Math.Min(temp.Position.X, distancesMiddleLine[1]);
                        transformedMesh.vertices[i] = temp;
                    }
                }
                else if (meshLengthX / distance > 1) // Scale down
                {
                    transformedMesh.ScaleX(distance / meshLengthX);
                    meshLengthX = distance;

                    for (int i = 0; i < transformedMesh.Vertices.Count; i++)
                    {
                        MyOwnVertex.PositionNormalTextured temp = transformedMesh.Vertices[i];
                        temp.Position.X = Math.Min(temp.Position.X, distance);
                        transformedMesh.vertices[i] = temp;
                    }
                }

                // Calculates the directions
                directionToNextPoint.Add(directionStart);
                for (int i = 1; i < middleline.Count - 1; i++)
                {
                    Vector3 direction = middleline[i + 1] - middleline[i - 1];
                    directionToNextPoint.Add(Vector3.Normalize(direction));
                }
                directionToNextPoint.Add(directionEnd);

                // Add all possible x-coordinates to zhe list.
                List<float> allXPositions = new List<float>();
                for (int i = 0; i < transformedMesh.Vertices.Count; i++)
                {
                    float xPosition = transformedMesh.vertices[i].X;
                    if (!allXPositions.Contains(xPosition))
                        allXPositions.Add(xPosition);
                }
                if (transformedMesh.vertices.Count == 0)
                {
                    allXPositions.Add(meshLengthX);
                }

                allXPositions.Sort();
                // Add the x Positions to the  middleLine
                for (int i = 0; i < allXPositions.Count; i++)
                {
                    if (!distancesMiddleLine.Contains(allXPositions[i]))
                    {
                        for (int j = 0; j < distancesMiddleLine.Count; j++)
                        {
                            if (allXPositions[i] < distancesMiddleLine[j])
                            {
                                //Add the new XPosition to the middleline and calculate the angle and all
                                // other data if needed.
                                float value = (allXPositions[i] - distancesMiddleLine[j - 1]) / (distancesMiddleLine[j] - distancesMiddleLine[j - 1]);
                                float distanceToPreviewPoint = allXPositions[i] - distancesMiddleLine[j - 1];
                                Vector3 differenceVector = Vector3.Subtract(directionToNextPoint[j], directionToNextPoint[j - 1]);
                                distancesMiddleLine.Insert(j, allXPositions[i]);

                                // add a new Point to the middleLine.
                                Vector3 direction = Vector3.Normalize(middleline[j] - middleline[j - 1]);
                                middleline.Insert(j, middleline[j - 1] + direction * distanceToPreviewPoint);

                                // insert new inclination Point.
                                inclinationVectors.Insert(j, Vector2.Add(inclinationVectors[j - 1], (Vector2.Multiply(inclinationVectors[j] - inclinationVectors[j - 1], value))));

                                // insert new direction to next point.
                                directionToNextPoint.Insert(j, Vector3.Subtract(directionToNextPoint[j], differenceVector * (1.0f - value)));
                                break;
                            }
                        }
                    }
                }

                int lastUsedMiddleLinePoint = 0;
                for (int i = 0; i < transformedMesh.Vertices.Count; i++)
                {
                    MyOwnVertex.PositionNormalTextured vertex = transformedMesh.Vertices[i];

                    int index = distancesMiddleLine.IndexOf(transformedMesh.Vertices[i].X);

                    if (index == -1)
                        index = middleline.Count - 1;

                    lastUsedMiddleLinePoint = Math.Max(lastUsedMiddleLinePoint, index);
                    Vector3 positionOnMiddleLine = middleline[index];
                    Vector3 directionVector = directionToNextPoint[index];
                    directionVector.Normalize();
                    Vector2 inclinationOfRoad = inclinationVectors[index];

                    Vector2 direction2D = new Vector2(directionVector.X, directionVector.Z);
                    direction2D.Normalize();

                    float length = (float)Math.Sqrt(directionVector.X * directionVector.X + directionVector.Z * directionVector.Z) - (float)Math.Sqrt(directionVector.Y * directionVector.Y);

                    // Start with the middleroad position
                    vertex.Position = positionOnMiddleLine;

                    // add the mesh vertex postion
                    Vector3 vertexAfterInclination = GeoMath.AddInclinationToVector3(inverseInclination, inclinationOfRoad, mesh.vertices[i].Position);
                    Vector3 normaleAfterInclination = GeoMath.AddInclinationToVector3(inverseInclination, inclinationOfRoad, mesh.vertices[i].Normal);

                    vertex.Position.X += -direction2D.Y * vertexAfterInclination.Z;
                    vertex.Position.Y += vertexAfterInclination.Y * (length + Math.Abs(directionVector.Y));
                    vertex.Position.Z += direction2D.X * vertexAfterInclination.Z;

                    // add the inclination
                    vertex.Position.X += vertexAfterInclination.Y * directionVector.Y * (-directionVector.X); //+ mesh.vertices[i].Y * inclinationOfRoad.Y;
                    vertex.Position.Z += vertexAfterInclination.Y * directionVector.Y * (-directionVector.Z);// +mesh.vertices[i].Y * inclinationOfRoad.Y;

                    // Calculate the normal
                    vertex.Normal.X = -direction2D.Y * normaleAfterInclination.Z;
                    vertex.Normal.Y = normaleAfterInclination.Y * (length + Math.Abs(directionVector.Y));
                    vertex.Normal.Z = direction2D.X * normaleAfterInclination.Z;
                    vertex.Normal.X += normaleAfterInclination.Y * directionVector.Y * (-directionVector.X);
                    vertex.Normal.Z += normaleAfterInclination.Y * directionVector.Y * (-directionVector.Z);
                    vertex.Normal.Normalize();

                    transformedMesh.Vertices[i] = vertex;
                }

                // When the road have no mesh we will use the max size.
                if (transformedMesh.vertices.Count == 0)
                {
                    int index = distancesMiddleLine.IndexOf(meshLengthX);

                    if (index == -1)
                        index = middleline.Count - 1;

                    lastUsedMiddleLinePoint = Math.Max(lastUsedMiddleLinePoint, index);
                    Vector3 positionOnMiddleLine = middleline[index];
                    Vector3 directionVector = directionToNextPoint[index];
                    directionVector.Normalize();
                }

                middleline.RemoveRange(0, lastUsedMiddleLinePoint);
                directionToNextPoint.RemoveRange(0, lastUsedMiddleLinePoint);
                inclinationVectors.RemoveRange(0, lastUsedMiddleLinePoint);

                if (middleline.Count == 1)
                    middleline.Clear();

                if (directionToNextPoint.Count > 1)
                {
                    directionStart = directionToNextPoint[0];
                    directionEnd = directionToNextPoint[directionToNextPoint.Count - 1];
                }
            }
            return transformedMesh;
        }

        public void AddGameVertices(string gameName, List<IGameVertex> gameVertices)
        {
            lock (this.thisLock)
            {
                if (this.verticesWr2Data.TryGetValue(gameName, out List<IGameVertex> gameVerticesOut))
                {
                    gameVerticesOut.AddRange(gameVertices);
                }
                else
                    this.verticesWr2Data.Add(gameName, gameVertices);
            }
        }

        public void AddMaterial(string addinName, string materialName, IMaterial material)
        {
            this.materialStorage.AddMaterial(addinName, materialName, material);
        }

        public void AddMesh(Mesh3dData mesh)
        {
            lock (this.thisLock)
            {
                ushort currentNumberVertices = (ushort)this.vertices.Count;

                // Add all Vertices to the current mesh
                this.vertices.AddRange(mesh.vertices);

                // Copy old gamevertices to new dictionary.

                foreach (var item in mesh.verticesWr2Data)
                {
                    if (this.verticesWr2Data.TryGetValue(item.Key, out List<IGameVertex> gameVerticesToAdd))
                    {
                        gameVerticesToAdd.AddRange(item.Value);
                    }
                    else
                    {
                        this.verticesWr2Data.Add(item.Key, new List<IGameVertex>(item.Value));
                    }
                }

                // update the mesh indices and add the current count of vertices to the values
                for (int i = 0; i < mesh.indices.Count; i++)
                {
                    mesh.indices[i] += currentNumberVertices;
                }

                List<string> materialNames = mesh.materialStorage.GetMaterialNames();
                for (int t = 0; t < materialNames.Count; t++)
                {
                    AddPolygons(mesh.materialStorage.GetMaterialName(t), mesh.polyTextures[t], mesh);
                }
            }
        }

        public void Dispose()
        {
            this.indices.Clear();
            this.indices = null;
            this.polyTextures.Clear();
            this.polyTextures = null;
            this.vertices.Clear();
            this.vertices = null;
        }

        /// <summary>
        /// returns the gamevertices.
        /// </summary>
        /// <param name="addin"></param>
        /// <returns></returns>
        public List<IGameVertex> GetGamevertices(AddinManager.IAddin addin, bool createVerticesIfNotExists)
        {
            string gameName = addin.GetGameName();
            List<IGameVertex> gameVertices;
            if (this.verticesWr2Data.TryGetValue(gameName, out gameVertices))
            {
                return gameVertices;
            }
            else
            {
                gameVertices = new List<IGameVertex>(this.vertices.Count);

                if (createVerticesIfNotExists)
                {
                    foreach (var item in this.vertices)
                    {
                        gameVertices.Add(addin.CreateGameVertice(item));
                    }
                }

                this.verticesWr2Data.Add(gameName, gameVertices);
                return gameVertices;
            }

            throw new Exception("No Gamevertices found for this addin:" + gameName);
        }

        public List<IGameVertex> GetGamevertices(string gameDllName)
        {
            if (this.verticesWr2Data.TryGetValue(gameDllName, out List<IGameVertex> Out))
                return Out;

            throw new Exception("Game vertices not found: " + gameDllName);
        }

        public IMaterial GetMaterial(string addinName, int materialID)
        {
            return this.materialStorage.GetMaterial(addinName, materialID);
        }

        public void RemoveMaterialID(int materialID)
        {
            this.materialStorage.RemoveMaterial(materialID);
        }

        public void RemoveUnusedVertices()
        {
            lock (this.thisLock)
            {
                bool[] usedVertices = new bool[this.vertices.Count];

                // Get the list of used vertices
                for (int i = 0; i < this.indices.Count; i++)
                {
                    usedVertices[this.indices[i]] = true;
                }

                // Remove unused vertices
                for (int i = this.vertices.Count - 1; i >= 0; i--)
                {
                    if (!usedVertices[i])
                    {
                        foreach (var gamename in this.verticesWr2Data.Keys)
                        {
                            this.verticesWr2Data[gamename].RemoveAt(i);
                        }

                        this.vertices.RemoveAt(i);

                        for (int j = 0; j < this.indices.Count; j++) // give the index list new ids for vertices.
                        {
                            if (this.indices[j] > i)
                                this.indices[j]--;
                        }
                    }
                }
            }
        }

        public void Rotation(Matrix rotation)
        {
            MyOwnVertex.PositionNormalTextured vertex;
            for (int v = 0; v < this.Vertices.Count; v++)
            {
                vertex = this.Vertices[v];
                vertex.Position = Vector3.TransformCoordinate(vertex.Position, rotation);
                vertex.Normal = Vector3.TransformCoordinate(vertex.Normal, rotation);
                this.Vertices[v] = vertex;
            }
        }

        public void SaveToStream(BinaryWriter bw, string modelFilename, AddinManager.AddinManager addinmanager)
        {
            // SaveVersion1(bw); // before 01.12.2016
            string extension = Path.GetExtension(modelFilename).ToUpper();

            switch (extension)
            {
                case ".WRMESH":
                    SaveVersion2(bw, modelFilename, addinmanager); // since 01.02.2017
                    break;

                case ".WRMESH2":
                    SaveVersion3(bw, modelFilename, addinmanager); // since 05.05.2017
                    break;
            }
        }

        internal static List<IGameVertex> LoadGameVerticesFromStream(BinaryReader br, Engine engine, out string addinname)
        {
            string version = br.ReadString();
            switch (version)
            {
                case "GameVerticesV1":
                    addinname = br.ReadString();
                    int numberOfVertices = br.ReadInt32();
                    int gameVertexVersion = br.ReadInt32();

                    var addin = engine.addinManager.GetAddin(addinname);

                    return addin.LoadVerticesFromStream(numberOfVertices, br, 0, gameVertexVersion);

                default:
                    throw new Exception("Wrong File-Version!\n" + version);
            }
        }

        internal string[] GetGameVertexInfos()
        {
            string[] addinNames = new string[this.verticesWr2Data.Keys.Count];
            this.verticesWr2Data.Keys.CopyTo(addinNames, 0);
            return addinNames;
        }

        internal void SaveGameVerticesToStream(BinaryWriter bw, string addinname)
        {
            var gameverties = GetGamevertices(addinname);

            bw.Write("GameVerticesV1");// Version1
            bw.Write(addinname); // addinname
            bw.Write((int)gameverties.Count); // number of vertices
            bw.Write((int)gameverties[0].GetCurrentVersion());

            foreach (var item in gameverties)
            {
                item.ToStream(bw);
            }
        }

        internal void SaveMaterialsToStream(BinaryWriter bw, string filename)
        {
            this.materialStorage.SaveToStream(bw.BaseStream);
        }

        #endregion Public Methods

        #region Private Methods

        private static int GetStartOfPolygon(Mesh3dData mesh, string materialName)
        {
            int startAdress = 0;
            for (int i = 0; i < mesh.polyTextures.Count; i++)
            {
                if (mesh.materialStorage.GetMaterialName(i) == materialName)
                {
                    return startAdress;
                }
                else
                {
                    startAdress += mesh.polyTextures[i];
                }
            }
            return -1;
        }

        private static Mesh3dData LoadV1(BinaryReader br, AddinManager.IAddin gameDll, AddinManager.AddinManager addinManager, MaterialManager materialManager)
        {
            if (gameDll.GetGameName() != "World Racing 2")
                throw new Exception("This mesh has an old file version and can only loaded with World Racing 2 addin!");

            Mesh3dData mesh = new Mesh3dData(addinManager, gameDll, new List<Material>() { });
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
            BinaryReader brWr2Data = new BinaryReader(ms);

            int nIndices = br.ReadInt32();
            int nVerts = br.ReadInt32();
            int nMaterials = br.ReadInt32();

            // Read Indices (each 2 Bytes)
            for (int i = 0; i < nIndices; i++)
            {
                mesh.indices.Add(br.ReadUInt16());
            }
            // Read engine vertices (position, normal, UV-texturecoordinates)
            for (int v = 0; v < nVerts; v++)
            {
                //MyOwnVertex.MyPositionNormalTextured vertex = MyOwnVertex.MyPositionNormalTextured.FromStream(br);
                MyOwnVertex.PositionNormalTextured vertex = MyOwnVertex.PositionNormalTextured.FromStream(br);
                mesh.vertices.Add(vertex);

                br.BaseStream.Position += 8; // overjump 2 extra uv coordinates.
                                             // Write  World Racing 2 vertex to memory stream. the stream will later loaded with Wr2-Addin.
                                             // Diffuse and vertex color must be switched. because of wrong saveorder in old files.
                byte[] diffuseColorData = br.ReadBytes(16);

                byte[] vertexColorData = br.ReadBytes(16);
                bw.Write(vertexColorData);
                bw.Write(diffuseColorData);
            }
            ms.Position = 0;
            int vertexVersion = 0; // only for World Racing 2 vertices.

            var gameVertices = gameDll.LoadVerticesFromStream(mesh.vertices, nVerts, brWr2Data, (int)brWr2Data.BaseStream.Length, vertexVersion);
            mesh.verticesWr2Data.Add(gameDll.GetGameName(), gameVertices);

            // Read the materials and the number of polygons
            for (int m = 0; m < nMaterials; m++)
            {
                // Number of polygons which must painted with this material
                mesh.polyTextures.Add(br.ReadInt32());
                // Write the Material
                var material = Material.FromStream(br, gameDll, materialManager);
                mesh.materialStorage.AddMaterial(gameDll.GetGameName(), material.MaterialName, material.GameMaterialProperties);
            }

            return mesh;
        }

        private static Mesh3dData LoadV2(BinaryReader br, AddinManager.AddinManager addinManager, MaterialManager materialManager, string filenameModel)
        {
            //throw new Exception("Outdated! Vertex version of gamevertices could not loaded with this method.");

            Mesh3dData mesh = new Mesh3dData(addinManager, addinManager.CurrentAddin, new List<Material>() { });

            int nIndices = br.ReadInt32();
            int nVerts = br.ReadInt32();
            int nMaterials = br.ReadInt32();

            // Read Indices (each 2 Bytes)
            for (int i = 0; i < nIndices; i++)
            {
                mesh.indices.Add(br.ReadUInt16());
            }

            // Read engine vertices (position, normal, UV-texturecoordinates)
            for (int v = 0; v < nVerts; v++)
            {
                MyOwnVertex.PositionNormalTextured vertex = MyOwnVertex.PositionNormalTextured.FromStream(br);
                mesh.vertices.Add(vertex);
            }

            // materialFilename xml
            string materialFilename = br.ReadString();
            List<string> materialFilenames = new List<string>();

            // Read the materials and the number of polygons
            for (int m = 0; m < nMaterials; m++)
            {
                // Number of polygons which must painted with this material
                mesh.polyTextures.Add(br.ReadInt32());
                // Read the materialname
                materialFilenames.Add(br.ReadString());
                //mesh.materials.Add(Material.FromStream(br, addinManager.CurrentAddin, materialManager));
            }

            // Get the name and the size of each gamevertex list and write it to the memory stream.
            int numberOfstoredVertexBlocks = br.ReadInt32();

            string[] addinNames = new string[numberOfstoredVertexBlocks];
            long[] sizeOfVertexblock = new long[numberOfstoredVertexBlocks];

            for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            {
                addinNames[i] = br.ReadString();
                sizeOfVertexblock[i] = br.ReadInt64();
            }

            for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            {
                IAddin addin;

                try
                {
                    addin = addinManager.GetAddin(addinNames[i]);
                    mesh.verticesWr2Data.Add(addinNames[i], addin.LoadVerticesFromStream(mesh.vertices.Count, br, (int)sizeOfVertexblock[i], 0));
                }
                catch (Exception)
                {
                    Console.WriteLine("Addin not found: " + addinNames[i]);
                    br.BaseStream.Position += sizeOfVertexblock[i];
                    continue;
                }
            }

            // Load materialstorage xml.file
            string pathToMaterialFile = Path.GetDirectoryName(filenameModel) + "\\";
            mesh.materialStorage = MaterialStorage.FromFile(pathToMaterialFile + materialFilename, addinManager, materialManager);

            // Add materials to materiallist
            for (int i = 0; i < nMaterials; i++)
            {
                var material = new Material(mesh.materialStorage.GetMaterial(addinManager.CurrentAddin.GetGameName(), materialFilenames[i]));
                material.MaterialName = materialFilenames[i];
                mesh.materialStorage.AddMaterial(addinManager.CurrentAddin.GetGameName(), material.MaterialName, material.GameMaterialProperties);
            }

            return mesh;
        }

        private static Mesh3dData LoadV3(BinaryReader br, AddinManager.AddinManager addinManager, MaterialManager materialManager, string filenameModel)
        {
            Mesh3dData mesh = new Mesh3dData(addinManager, addinManager.CurrentAddin, new List<Material>() { });

            int nIndices = br.ReadInt32();
            int nVerts = br.ReadInt32();
            int nMaterials = br.ReadInt32();

            // Read Indices (each 2 Bytes)
            for (int i = 0; i < nIndices; i++)
            {
                mesh.indices.Add(br.ReadUInt16());
            }

            // Read engine vertices (position, normal, UV-texturecoordinates)
            for (int v = 0; v < nVerts; v++)
            {
                MyOwnVertex.PositionNormalTextured vertex = MyOwnVertex.PositionNormalTextured.FromStream(br);
                mesh.vertices.Add(vertex);
            }

            // materialFilename xml
            string materialFilename = br.ReadString();
            List<string> materialFilenames = new List<string>();

            // Read the materials and the number of polygons
            for (int m = 0; m < nMaterials; m++)
            {
                // Number of polygons which must painted with this material
                mesh.polyTextures.Add(br.ReadInt32());
                // Read the materialname
                materialFilenames.Add(br.ReadString());
                //mesh.materials.Add(Material.FromStream(br, addinManager.CurrentAddin, materialManager));
            }

            // Get the name and the size of each gamevertex list and write it to the memory stream.
            int numberOfstoredVertexBlocks = br.ReadInt32();

            string[] addinNames = new string[numberOfstoredVertexBlocks];
            long[] sizeOfVertexblock = new long[numberOfstoredVertexBlocks];
            int[] vertexVersion = new int[numberOfstoredVertexBlocks];

            for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            {
                addinNames[i] = br.ReadString();
                sizeOfVertexblock[i] = br.ReadInt64();
                vertexVersion[i] = br.ReadInt32();
            }

            for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            {
                IAddin addin;

                try
                {
                    addin = addinManager.GetAddin(addinNames[i]);
                    mesh.verticesWr2Data.Add(addinNames[i], addin.LoadVerticesFromStream(mesh.vertices.Count, br, (int)sizeOfVertexblock[i], vertexVersion[i]));
                }
                catch (Exception)
                {
                    Console.WriteLine("Addin not found: " + addinNames[i]);
                    br.BaseStream.Position += sizeOfVertexblock[i];
                    continue;
                }
            }

            // Load materialstorage xml.file
            string pathToMaterialFile = Path.GetDirectoryName(filenameModel) + "\\";
            mesh.materialStorage = MaterialStorage.FromFile(pathToMaterialFile + materialFilename, addinManager, materialManager);

            // Add materials to materiallist
            for (int i = 0; i < nMaterials; i++)
            {
                IMaterial gameMaterial = null;
                try
                {
                    gameMaterial = mesh.materialStorage.GetMaterial(addinManager.CurrentAddin.GetGameName(), materialFilenames[i]);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                // Create new Material if it is null
                if (gameMaterial == null)
                    gameMaterial = addinManager.CurrentAddin.CreateEmptyMaterial(materialManager);

                var material = new Material(gameMaterial);
                material.MaterialName = materialFilenames[i];
                mesh.materialStorage.AddMaterial(addinManager.CurrentAddin.GetGameName(), material.MaterialName, material.GameMaterialProperties);
            }

            return mesh;
        }

        private static Mesh3dData LoadV4(BinaryReader br, AddinManager.AddinManager addinManager, MaterialManager materialManager, MaterialStorage materialStorage)
        {
            Mesh3dData mesh = new Mesh3dData(addinManager, addinManager.CurrentAddin, new List<Material>() { });

            int nIndices = br.ReadInt32();
            int nVerts = br.ReadInt32();
            int nMaterials = br.ReadInt32();

            // Read Indices (each 2 Bytes)
            for (int i = 0; i < nIndices; i++)
            {
                mesh.indices.Add(br.ReadUInt16());
            }

            // Read engine vertices (position, normal, UV-texturecoordinates)
            for (int v = 0; v < nVerts; v++)
            {
                MyOwnVertex.PositionNormalTextured vertex = MyOwnVertex.PositionNormalTextured.FromStream(br);
                mesh.vertices.Add(vertex);
            }

            // materialFilename xml
            // string materialFilename = br.ReadString();
            List<string> materialFilenames = new List<string>();

            // Read the materials and the number of polygons
            for (int m = 0; m < nMaterials; m++)
            {
                // Number of polygons which must painted with this material
                mesh.polyTextures.Add(br.ReadInt32());
                // Read the materialname
                materialFilenames.Add(br.ReadString());
                //mesh.materials.Add(Material.FromStream(br, addinManager.CurrentAddin, materialManager));
            }

            // Get the name and the size of each gamevertex list and write it to the memory stream.
            //int numberOfstoredVertexBlocks = br.ReadInt32();

            //string[] addinNames = new string[numberOfstoredVertexBlocks];
            //long[] sizeOfVertexblock = new long[numberOfstoredVertexBlocks];
            //int[] vertexVersion = new int[numberOfstoredVertexBlocks];

            //for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            //{
            //    addinNames[i] = br.ReadString();
            //    sizeOfVertexblock[i] = br.ReadInt64();
            //    vertexVersion[i] = br.ReadInt32();
            //}

            //for (int i = 0; i < numberOfstoredVertexBlocks; i++)
            //{
            //    IAddin addin;

            //    try
            //    {
            //        addin = addinManager.GetAddin(addinNames[i]);
            //        mesh.verticesWr2Data.Add(addinNames[i], addin.LoadVerticesFromStream(mesh.vertices.Count, br, (int)sizeOfVertexblock[i], vertexVersion[i]));
            //    }
            //    catch (Exception)
            //    {
            //        Console.WriteLine("Addin not found: " + addinNames[i]);
            //        br.BaseStream.Position += sizeOfVertexblock[i];
            //        continue;
            //    }
            //}

            // Load materialstorage xml.file
            //string pathToMaterialFile = Path.GetDirectoryName(filenameModel) + "\\";
            mesh.materialStorage = materialStorage;// MaterialStorage.FromFile(pathToMaterialFile + materialFilename, addinManager, materialManager);

            // Add materials to materiallist
            for (int i = 0; i < nMaterials; i++)
            {
                IMaterial gameMaterial = null;
                try
                {
                    gameMaterial = mesh.materialStorage.GetMaterial(addinManager.CurrentAddin.GetGameName(), materialFilenames[i]);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                // Create new Material if it is null
                if (gameMaterial == null)
                    gameMaterial = addinManager.CurrentAddin.CreateEmptyMaterial(materialManager);

                var material = new Material(gameMaterial);
                material.MaterialName = materialFilenames[i];
                mesh.materialStorage.AddMaterial(addinManager.CurrentAddin.GetGameName(), material.MaterialName, material.GameMaterialProperties);
            }

            return mesh;
        }

        private void AddPolygons(string materialName, int numberOfPolygons, Mesh3dData mesh)
        {
            lock (this.thisLock)
            {
                int startAddress = GetStartOfPolygon(mesh, materialName);
                int currentPolygon = 0;

                //try to find the same material in the current mesh and add the other there
                int materialID = this.materialStorage.GetMaterialID(materialName);

                if (materialID > -1) // If the material is already in the mesh
                {
                    for (int i = 0; i < materialID; i++)
                    {
                        currentPolygon += this.polyTextures[i];
                    }

                    this.polyTextures[materialID] += numberOfPolygons;

                    //add the vertices and indices;
                    for (int i = 0; i < numberOfPolygons; i++)
                    {
                        int offsetIndices = startAddress * 3;
                        this.indices.Insert((currentPolygon * 3), (mesh.indices[(i + startAddress) * 3]));
                        this.indices.Insert((currentPolygon * 3) + 1, (mesh.indices[(i + startAddress) * 3 + 1]));
                        this.indices.Insert((currentPolygon * 3) + 2, (mesh.indices[(i + startAddress) * 3 + 2]));

                        currentPolygon++;
                    }
                    return;
                }

                for (int i = 0; i < mesh.materialStorage._materialsAddin.Count; i++)
                {
                    string addinName = mesh.materialStorage._materialsAddin[i].Addinname;

                    try
                    {
                        //When no Material is Found add the Polys at the  end of the file;
                        this.materialStorage.AddMaterial(addinName, materialName, mesh.materialStorage.GetMaterial(addinName, materialName));
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
                this.polyTextures.Add(numberOfPolygons);

                for (int i = 0; i < numberOfPolygons; i++)
                {
                    this.indices.Add(mesh.indices[startAddress * 3]);
                    this.indices.Add(mesh.indices[startAddress * 3 + 1]);
                    this.indices.Add(mesh.indices[startAddress * 3 + 2]);
                    startAddress++;
                }
            }
        }

        private void SaveVersion1(BinaryWriter bw)
        {
            throw new Exception("This Save-Method is no longer supported!!");

            // comment out... 21.05.2017

            /*
            // Write Header (26 Bytes)
            bw.Write("MeshForAddin 1".ToCharArray());   //14 Bytes Identy
            bw.Write(this.indices.Count);               // 4 Bytes Number of indices
            bw.Write(this.vertices.Count);              // 4 Bytes Number of vertices
            bw.Write(this.materials.Count);             // 4 Bytes Number of materials

            // Write Indices (each 2 Bytes)
            for (int i = 0; i < this.indices.Count; i++)
            {
                bw.Write(this.indices[i]);
            }

            // Write vertices
            for (int v = 0; v < this.vertices.Count; v++)
            {
                throw new NotImplementedException();

                //MyOwnVertex.MyPositionNormalTextured vertex = new MyOwnVertex.MyPositionNormalTextured(
                //   this.vertices[v].Position, this.vertices[v].Normal,
                //   this.verticesWr2Data[v].Tu, this.verticesWr2Data[v].Tv,
                //   this.verticesWr2Data[v].Tu2, this.verticesWr2Data[v].Tv2,
                //   this.verticesWr2Data[v].DiffuseColor, this.verticesWr2Data[v].VertexColor);

                // vertex.SaveToStream(bw);
            }

            // Write the materials and the number of polygons
            for (int m = 0; m < this.materials.Count; m++)
            {
                // Number of polygons which must painted with this material
                bw.Write(this.polyTextures[m]);
                // Write the Material
                this.materials[m].SaveToStream(bw);
            }
            */
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="bw"></param>
        /// <param name="modelFilename"></param>
        private void SaveVersion2(BinaryWriter bw, string modelFilename, AddinManager.AddinManager addinmanger)
        {
            throw new Exception("This Save-Method is no longer supported!!");

            // comment out... 21.05.2017

            /*
            string pathToModel = Path.GetDirectoryName(modelFilename);
            // Create materila filename
            string materialFilename = Path.GetFileNameWithoutExtension(modelFilename) + ".xml";
            this.materialStorage.SaveToFile(pathToModel + "\\" + materialFilename);

            // Write Header (26 Bytes)
            bw.Write("MeshForAddin 3".ToCharArray());   //14 Bytes Identy
            bw.Write(this.indices.Count);               // 4 Bytes Number of indices
            bw.Write(this.vertices.Count);              // 4 Bytes Number of vertices
            bw.Write(this.materials.Count);             // 4 Bytes Number of materials

            // Write Indices (each 2 Bytes)
            for (int i = 0; i < this.indices.Count; i++)
            {
                bw.Write(this.indices[i]);
            }

            // Write engine vertices
            for (int v = 0; v < this.vertices.Count; v++)
            {
                this.vertices[v].ToStream(bw);
            }

            // Write materialFilename
            bw.Write(materialFilename);

            // Write the materials and the number of polygons
            for (int m = 0; m < this.materials.Count; m++)
            {
                // Number of polygons which must painted with this material
                bw.Write(this.polyTextures[m]);

                // Set materialname if it is empty
                if (this.materials[m].MaterialName == string.Empty)
                    this.materials[m].MaterialName = "Material " + m.ToString();

                // Write the Materialname
                bw.Write(this.materials[m].MaterialName);
            }

            // Delete gamevertices which does not match the number of gamevertices;
            List<string> addinDataToDelete = new List<string>();
            foreach (var item in this.verticesWr2Data)
            {
                if (item.Value.Count != this.vertices.Count)
                    addinDataToDelete.Add(item.Key);
            }

            foreach (var item in addinDataToDelete)
            {
                this.verticesWr2Data.Remove(item);
            }

            WriteGameVerticesToStream(bw);

            //// Get the name and the size of each gamevertex list and write it to the memory stream.
            //string[] addinName = new string[this.verticesWr2Data.Count];
            //long[] sizeInBytes = new long[this.verticesWr2Data.Count];
            //int[] vertexVersion = new int[this.verticesWr2Data.Count];
            //long currentStreamOffset = 0;

            //MemoryStream ms = new MemoryStream();
            //BinaryWriter bwForGameVertices = new BinaryWriter(ms);
            //int index = 0;

            //foreach (var item in this.verticesWr2Data)
            //{
            //    if (item.Value.Count > 0)
            //    {
            //        addinName[index] = item.Key; // Addin name
            //        vertexVersion[index] = item.Value[0].GetCurrentVersion();

            //        foreach (var vertex in item.Value)
            //        {
            //            vertex.ToStream(bwForGameVertices);
            //        }
            //    }

            //    sizeInBytes[index] = bwForGameVertices.BaseStream.Position - currentStreamOffset; // store the number of bytes for vertexdata.
            //    currentStreamOffset = bwForGameVertices.BaseStream.Position; // Offset to calculate the used bytes for each list.

            //    index++;
            //}

            ////  Write number of gamevertex lists and its names.
            //bw.Write(addinName.Length); // number of vertexdata for addins.
            //for (int i = 0; i < addinName.Length; i++)
            //{
            //    // AddinName
            //    bw.Write(addinName[i]);
            //    bw.Write(sizeInBytes[i]);
            //    bw.Write(vertexVersion[i]);
            //}

            //// Write the memorystream (which stored the complete gamevertexdata) to mainstream.
            //ms.Position = 0;
            //using (BinaryReader br = new BinaryReader(ms))
            //    bw.Write(br.ReadBytes((int)ms.Length));
            */
        }

        /// <summary>
        /// since 05.05.2017
        /// </summary>
        /// <param name="bw"></param>
        /// <param name="modelFilename"></param>
        /// <param name="addinmanger"></param>
        private void SaveVersion3(BinaryWriter bw, string modelFilename, AddinManager.AddinManager addinmanger)
        {
            //throw new NotImplementedException("See details");
            // TODO: Gamevertices

            string pathToModel = Path.GetDirectoryName(modelFilename);
            // Create materila filename
            string materialFilename = Path.GetFileNameWithoutExtension(modelFilename) + ".xml";
            //this.materialStorage.SaveToFile(pathToModel + "\\" + materialFilename);
            List<string> materialNames = this.materialStorage.GetMaterialNames();

            // Write Header (26 Bytes)
            bw.Write("MeshForAddin 4".ToCharArray());   //14 Bytes Identy
            bw.Write(this.indices.Count);               // 4 Bytes Number of indices
            bw.Write(this.vertices.Count);              // 4 Bytes Number of vertices
            bw.Write(materialNames.Count);             // 4 Bytes Number of materials

            // Write Indices (each 2 Bytes)
            for (int i = 0; i < this.indices.Count; i++)
            {
                bw.Write(this.indices[i]);
            }

            // Write engine vertices
            for (int v = 0; v < this.vertices.Count; v++)
            {
                this.vertices[v].ToStream(bw);
            }

            // Write materialFilename
            // bw.Write(materialFilename);

            // Write the materials and the number of polygons
            for (int m = 0; m < materialNames.Count; m++)
            {
                // Number of polygons which must painted with this material
                bw.Write(this.polyTextures[m]);

                // Set materialname if it is empty
                if (materialNames[m] == string.Empty)
                    materialNames[m] = "Material " + m.ToString();

                // Write the Materialname
                bw.Write(materialNames[m]);
            }

            // Delete gamevertices which does not match the number of gamevertices;
            List<string> addinDataToDelete = new List<string>();
            foreach (var item in this.verticesWr2Data)
            {
                if (item.Value.Count != this.vertices.Count)
                    addinDataToDelete.Add(item.Key);
            }

            foreach (var item in addinDataToDelete)
            {
                this.verticesWr2Data.Remove(item);
            }
        }

        private void ScaleX(float scale)
        {
            for (int i = 0; i < this.vertices.Count; i++)
            {
                MyOwnVertex.PositionNormalTextured vertex = this.Vertices[i];
                vertex.X *= scale;
                this.Vertices[i] = vertex;
            }
        }

        private void WriteGameVerticesToStream(BinaryWriter bw)
        {
            // Get the name and the size of each gamevertex list and write it to the memory stream.
            string[] addinName = new string[this.verticesWr2Data.Count];
            long[] sizeInBytes = new long[this.verticesWr2Data.Count];
            int[] vertexVersion = new int[this.verticesWr2Data.Count];
            long currentStreamOffset = 0;

            MemoryStream ms = new MemoryStream();
            BinaryWriter bwForGameVertices = new BinaryWriter(ms);
            int index = 0;

            foreach (var item in this.verticesWr2Data)
            {
                if (item.Value.Count > 0)
                {
                    addinName[index] = item.Key; // Addin name
                    vertexVersion[index] = item.Value[0].GetCurrentVersion();

                    foreach (var vertex in item.Value)
                    {
                        vertex.ToStream(bwForGameVertices);
                    }
                }

                sizeInBytes[index] = bwForGameVertices.BaseStream.Position - currentStreamOffset; // store the number of bytes for vertexdata.
                currentStreamOffset = bwForGameVertices.BaseStream.Position; // Offset to calculate the used bytes for each list.

                index++;
            }

            //  Write number of gamevertex lists and its names.
            bw.Write(addinName.Length); // number of vertexdata for addins.
            for (int i = 0; i < addinName.Length; i++)
            {
                // AddinName
                bw.Write(addinName[i]);
                bw.Write(sizeInBytes[i]);
                bw.Write(vertexVersion[i]);
            }

            // Write the memorystream (which stored the complete gamevertexdata) to mainstream.
            ms.Position = 0;
            using (BinaryReader br = new BinaryReader(ms))
                bw.Write(br.ReadBytes((int)ms.Length));
        }

        #endregion Private Methods
    }
}